﻿//-------------------------------------------------------------------------------------------------
// <copyright file="Neo4JNode.cs" company="BeeWix">
// BeeWix. Licensed under MIT
// </copyright>
//-------------------------------------------------------------------------------------------------

namespace BeeWix.Toolkit.NoSql.Neo4J
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using BeeWix.Toolkit.Json;
    using System.Xml.Serialization;

    /// <summary>
    /// Base class that allow low level node manipulation
    /// </summary>
    public class Neo4JNode : Neo4JBase
    {
        /// <summary>
        /// Private value for the AlwaysSynchronised property
        /// </summary>
        [XmlIgnore]
        private bool pAlwaysSynchronised = false;

        /// <summary>
        /// Initializes a new instance of the <see cref="Neo4JNode" /> class.
        /// </summary>
        public Neo4JNode()
        {
        }

        /// <summary>
        /// Gets or sets a value indicating whether or not the object is directly synchronised with the database
        /// If true, Data are always retreived from teh database, and directly updated to the database
        /// </summary>
        [XmlIgnore]
        public bool AlwaysSynchronised
        {
            get
            {
                return this.pAlwaysSynchronised;
            }

            set
            {
                if (value && !string.IsNullOrWhiteSpace(this.jsonData))
                {
                    this.pAlwaysSynchronised = true;
                }
                else
                {
                    this.pAlwaysSynchronised = false;
                }
            }
        }

        /// <summary>
        /// Gets property string if defined
        /// </summary>
        /// <param name="propertyName">Property name</param>
        /// <returns>String value or null</returns>
        public string GetProperty(string propertyName)
        {
            return this.Data.data.IsDefined(propertyName) ? this.Data.data[propertyName].ToString() : null;
        }

        /// <summary>
        /// Gets the property value of type T
        /// </summary>
        /// <typeparam name="T">Returned type</typeparam>
        /// <param name="propertyName">Property name</param>
        /// <returns>Property value o ftype T</returns>
        public T GetProperty<T>(string propertyName)
        {
            try
            {
                string strTType = typeof(T).ToString();

                if (strTType == typeof(string).ToString())
                {
                    return this.Data.data.IsDefined(propertyName) ? this.Data.data[propertyName].ToString() : default(T);
                }

                if (strTType == typeof(int).ToString())
                {
                    return this.Data.data.IsDefined(propertyName) ? int.Parse(this.Data.data[propertyName].ToString()) : default(T);
                }

                if (strTType == typeof(long).ToString())
                {
                    return this.Data.data.IsDefined(propertyName) ? long.Parse(this.Data.data[propertyName].ToString()) : default(T);
                }

                if (strTType == typeof(short).ToString())
                {
                    return this.Data.data.IsDefined(propertyName) ? short.Parse(this.Data.data[propertyName].ToString()) : default(T);
                }

                if (strTType == typeof(float).ToString())
                {
                    return this.Data.data.IsDefined(propertyName) ? float.Parse(this.Data.data[propertyName].ToString()) : default(T);
                }

                if (strTType == typeof(double).ToString())
                {
                    return this.Data.data.IsDefined(propertyName) ? double.Parse(this.Data.data[propertyName].ToString()) : default(T);
                }

                if (strTType == typeof(decimal).ToString())
                {
                    return this.Data.data.IsDefined(propertyName) ? decimal.Parse(this.Data.data[propertyName].ToString()) : default(T);
                }

                if (strTType == typeof(bool).ToString())
                {
                    return this.Data.data.IsDefined(propertyName) ? bool.Parse(this.Data.data[propertyName].ToString()) : default(T);
                }

                if (strTType == typeof(DateTime).ToString())
                {
                    if (this.Data.data.IsDefined(propertyName))
                    {
                        string[] dateSec = this.Data.data[propertyName].ToString().Split('T');
                        var date = dateSec[0].Split('-');
                        var hour = dateSec[1].Split(':');

                        return (T)(object)new DateTime(int.Parse(date[0]), int.Parse(date[1]), int.Parse(date[2]), int.Parse(hour[0]), int.Parse(hour[1]), int.Parse(hour[2]));
                    }
                    else
                    {
                        return default(T);
                    }
                }

                return this.Data.data.IsDefined(propertyName) ? (T)this.Data.data[propertyName] : default(T);
            }
            catch
            {
                return default(T);
            }
        }

        /// <summary>
        /// Sets the property value
        /// </summary>
        /// <param name="propertyName">Property name</param>
        /// <param name="value">Value to set</param>
        public void SetProperty(string propertyName, object value)
        {
            if (!this.Data.IsDefined("data"))
                this.Data["data"] = new { };

            // DateTime object must be passed like that : "1980-03-27T00:00:00"
            if (value is DateTime)
            {
                this.Data.data[propertyName] = ((DateTime)value).Year + "-" +
                    ((DateTime)value).Month + "-" +
                    ((DateTime)value).Day + "T" +
                    ((DateTime)value).Hour.ToString().PadLeft(2, '0') + ":" +
                    ((DateTime)value).Minute.ToString().PadLeft(2, '0') + ":" +
                    ((DateTime)value).Minute.ToString().PadLeft(2, '0');
            }
            else if (value == null)
            {
                return;
            }            
            else
            {
                this.Data.data[propertyName] = value;
            }
        }

        public string ToJson()
        {
            return JSON.Instance.ToJSON(this, false, false, false, false);
        }

        public void FromJson(string json)
        {
            var dico = JSON.Instance.Parse(json) as Dictionary<string, object>;

            if (dico != null)
            {
                foreach (var itm in dico)
                {
                    var member = this.GetType().GetProperty(itm.Key, System.Reflection.BindingFlags.SetProperty | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);

                    try
                    {
                        string strTType = member.PropertyType.ToString();

                        if (strTType == typeof(string).ToString())
                        {
                            member.SetValue(this, itm.Value.ToString(), null);
                        }

                        if (strTType == typeof(int).ToString())
                        {
                            member.SetValue(this, int.Parse(itm.Value.ToString()), null);
                        }

                        if (strTType == typeof(long).ToString())
                        {
                            member.SetValue(this, long.Parse(itm.Value.ToString()), null);
                        }

                        if (strTType == typeof(short).ToString())
                        {
                            member.SetValue(this, short.Parse(itm.Value.ToString()), null);
                        }

                        if (strTType == typeof(float).ToString())
                        {
                            member.SetValue(this, float.Parse(itm.Value.ToString()), null);
                        }

                        if (strTType == typeof(double).ToString())
                        {
                            member.SetValue(this, double.Parse(itm.Value.ToString()), null);
                        }

                        if (strTType == typeof(decimal).ToString())
                        {
                            member.SetValue(this, decimal.Parse(itm.Value.ToString()), null);
                        }

                        if (strTType == typeof(bool).ToString())
                        {
                            member.SetValue(this, bool.Parse(itm.Value.ToString()), null);
                        }

                        if (strTType == typeof(DateTime).ToString())
                        {

                            string[] dateSec = itm.Value.ToString().Split('T');
                            var date = dateSec[0].Split('-');
                            var hour = dateSec[1].Split(':');

                            member.SetValue(this, new DateTime(int.Parse(date[0]), int.Parse(date[1]), int.Parse(date[2]), int.Parse(hour[0]), int.Parse(hour[1]), int.Parse(hour[2])), null);

                        }
                    }
                    catch
                    {

                    }
                }
            }
        }

        public void FromData()
        {
            if (this.Data.IsDefined("data"))
            {
                this.FromJson(this.Data.data.ToString());
            }
        }
    }
}
